#include "ParallelMM.h"
#include <stdio.h>
//#include <conio.h>
#include <time.h>
//#include <dos.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
//#include <windows.h> 
 #include <omp.h>
	
ParallelMM::ParallelMM(void)
{
}

ParallelMM::~ParallelMM(void)
{
}
// Function that converts numbers form LongInt type to double type
/*double LiToDouble (LARGE_INTEGER x) {
double result =
 ((double)x.HighPart) * 4.294967296E9 + (double)((x).LowPart);
 return result;
} */

	// Function that gets the timestamp in seconds
	/*double GetTime() {
	 LARGE_INTEGER lpFrequency, lpPerfomanceCount;
	 QueryPerformanceFrequency (&lpFrequency);
	 QueryPerformanceCounter (&lpPerfomanceCount);
	 return LiToDouble(lpPerfomanceCount)/LiToDouble(lpFrequency);
	} */
// Function for random initialization of matrix elements
void RandomDataInitialization (double* pAMatrix, double* pBMatrix,
 int Size) {
 int i, j; // Loop variables
 srand(unsigned(clock()));
 for (i=0; i<Size; i++)
 for (j=0; j<Size; j++) {
 pAMatrix[i*Size+j] = rand()/double(1000);
 pBMatrix[i*Size+j] = rand()/double(1000);
 }
} 

// Function for simple initialization of matrix elements
	void DummyDataInitialization(double* pAMatrix, double* pBMatrix, int Size){
	 int i, j; // Loop variables
	 for (i=0; i<Size; i++) {
	 for (j=0; j<Size; j++) {
	 pAMatrix[i*Size+j] = 1;
	 pBMatrix[i*Size+j] = 1;
	 }
	 }
	} 
	void PrintMatrix (double* pMatrix, int RowCount, int ColCount) 
	{int i, j; // Loop variables
	for (i=0; i<RowCount; i++) 
	{for (j=0; j<ColCount; j++)
	printf("%7.4f ", pMatrix[i*ColCount+j]);printf("\n");}
	}

void ProcessInitialization (double* &pAMatrix, double* &pBMatrix,
 double* &pCMatrix, int &Size) {
 // Setting the size of matricies

 do {
	 //printf("\nEnter size of matricies: ");
	 //scanf_s("%d", &Size);
	 Size=6000;
	 printf("\nChosen matricies size = %d\n", Size);
	 if (Size <= 0)
	 printf("\nSize of objects must be greater than 0!\n");
	 } while (Size <= 0); 

		// Memory allocation
	 pAMatrix = new double [Size*Size];
	 pBMatrix = new double [Size*Size];
	 pCMatrix = new double [Size*Size];

  /*DummyDataInitialization(pAMatrix, pBMatrix, Size);
 for (int i=0; i<Size*Size; i++) {
 pCMatrix[i] = 0;
 } */
	  // Random initialization of matrix elements
 RandomDataInitialization(pAMatrix, pBMatrix, Size);
 for (int i=0; i<Size*Size; i++) {
 pCMatrix[i] = 0;
 } 

} 
// Function for computational process termination
void ProcessTermination (double* pAMatrix, double* pBMatrix,
 double* pCMatrix) {
 delete [] pAMatrix;
 delete [] pBMatrix;
 delete [] pCMatrix;
} 
	// Function for matrix multiplication
void SerialResultCalculation(double* pAMatrix, double* pBMatrix,
 double* pCMatrix, int Size) {
 int i, j, k; // Loop variables
 for (i=0; i<Size; i++) {
 for (j=0; j<Size; j++) {
 for (k=0; k<Size; k++) {
 pCMatrix[i*Size+j] += pAMatrix[i*Size+k]*pBMatrix[k*Size+j];
 }
 }
 }
} 
void ParallelResultCalculation (double* pAMatrix, double* pBMatrix,
double* pCMatrix, int Size) {
		int i, j, k; // Loop variables
		
		int ThreadNum = 4;
		omp_set_num_threads(ThreadNum);
		#pragma omp parallel
		{
		//printf("Hello!\n");
		int ThreadID = omp_get_thread_num();
		printf("Thread ID: %d\n", ThreadID);
		#pragma omp for private(i,j,k)
		for (i=0; i<Size; i++) {
		 for (j=0; j<Size; j++) {
		 for (k=0; k<Size; k++) {
		 pCMatrix[i*Size+j] += pAMatrix[i*Size+k]*pBMatrix[k*Size+j];
		 }
		 }
		 }
		} // pragma omp parallel
}


int main(int argc, char* argv[]) 
{
 double* pAMatrix; // The first argument of matrix multiplication
 double* pBMatrix; // The second argument of matrix multiplication
 double* pCMatrix; // The result of matrix multiplication
 int Size; // Sizes of matricies

	 printf ("Serial matrix multiplication program\n");
	 //_getch(); 
	// Data initialization
	ProcessInitialization(pAMatrix, pBMatrix, pCMatrix, Size);
	//ParallelResultCalculation (pAMatrix, pBMatrix, pCMatrix, Size);		
	 
	
		// Matrix output
	 printf ("Initial A Matrix \n");
	// PrintMatrix(pAMatrix, Size, Size);
	 printf("Initial B Matrix \n");
	// PrintMatrix(pBMatrix, Size, Size); 

	 // Matrix multiplication
	double start = omp_get_wtime();
			// Matrix multiplication
	 //SerialResultCalculation(pAMatrix, pBMatrix, pCMatrix, Size);
	ParallelResultCalculation (pAMatrix, pBMatrix, pCMatrix, Size);	
double finish = omp_get_wtime();
	double duration = (finish-start);


	 //Printing the result matrix
	 printf ("\n Result Matrix: \n");
	 //PrintMatrix(pCMatrix, Size, Size);
	 // Computational process termination
	 ProcessTermination(pAMatrix, pBMatrix, pCMatrix); 
		//  Computational process termination
	 // Printing the time spent by matrix multiplication
	printf("\n Time of execution: %f\n", duration); 


 printf("Parallel matrix multiplication program\n");
 // Program termination
 //ProcessTermination(pAMatrix, pBMatrix, pCMatrix, Size); 
 return 0;
}